package com.rskj.cashbook.service.iml;

import com.rskj.cashbook.dao.AutoAccountingRuleDao;
import com.rskj.cashbook.service.AutoAccountingRuleService;
import com.rskj.cashbook.vo.AutoAccountingRule;
import com.rskj.cashbook.vo.AutoAccountingExecutionLog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 自动记账调度器服务
 * 负责定时检查和执行自动记账规则
 * @author system
 */
@Service
public class AutoAccountingSchedulerService {
    
    private static final Logger log = LoggerFactory.getLogger(AutoAccountingSchedulerService.class);
    
    @Autowired
    private AutoAccountingRuleService autoAccountingRuleService;
    
    @Autowired
    private AutoAccountingRuleDao autoAccountingRuleDao;
    
    // 创建线程池用于并发执行规则
    private final ExecutorService executorService = Executors.newFixedThreadPool(5);
    
    /**
     * 每分钟检查一次需要执行的规则
     */
    @Scheduled(fixedRate = 60000) // 60秒执行一次
    public void checkAndExecuteRules() {
        try {
            log.info("开始检查需要执行的自动记账规则...");
            
            LocalDateTime currentTime = LocalDateTime.now();
            List<AutoAccountingRule> rulesToExecute = autoAccountingRuleDao.selectRulesToExecute(currentTime);
            
            if (rulesToExecute.isEmpty()) {
                log.info("当前没有需要执行的规则");
                return;
            }
            
            log.info("发现 {} 个需要执行的规则", rulesToExecute.size());
            
            // 并发执行规则
            for (AutoAccountingRule rule : rulesToExecute) {
                CompletableFuture.runAsync(() -> {
                    try {
                        executeRuleAsync(rule);
                    } catch (Exception e) {
                        log.error("异步执行规则 {} 时发生错误: {}", rule.getId(), e.getMessage(), e);
                    }
                }, executorService);
            }
            
        } catch (Exception e) {
            log.error("检查自动记账规则时发生错误: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 每天凌晨2点执行一次完整的规则检查
     */
    @Scheduled(cron = "0 0 2 * * ?")
    public void dailyRuleCheck() {
        try {
            log.info("开始执行每日规则检查...");
            
            // 获取所有启用的规则
            List<AutoAccountingRule> activeRules = autoAccountingRuleDao.selectActiveRules();
            
            for (AutoAccountingRule rule : activeRules) {
                try {
                    // 重新计算下次执行时间
                    autoAccountingRuleService.calculateNextExecuteTime(rule);
                    
                    // 更新规则
                    autoAccountingRuleDao.update(rule);
                    
                    log.debug("更新规则 {} 的下次执行时间为: {}", rule.getRuleName(), rule.getNextExecuteTime());
                    
                } catch (Exception e) {
                    log.error("更新规则 {} 时发生错误: {}", rule.getId(), e.getMessage(), e);
                }
            }
            
            log.info("每日规则检查完成，共处理 {} 个规则", activeRules.size());
            
        } catch (Exception e) {
            log.error("执行每日规则检查时发生错误: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 每周日凌晨3点执行周规则检查
     */
    @Scheduled(cron = "0 0 3 ? * SUN")
    public void weeklyRuleCheck() {
        try {
            log.info("开始执行每周规则检查...");
            
            // 获取所有启用的规则
            List<AutoAccountingRule> activeRules = autoAccountingRuleDao.selectActiveRules();
            
            for (AutoAccountingRule rule : activeRules) {
                try {
                    // 检查规则是否需要调整
                    if ("WEEKLY".equals(rule.getFrequency())) {
                        // 重新计算下周的执行时间
                        autoAccountingRuleService.calculateNextExecuteTime(rule);
                        autoAccountingRuleDao.update(rule);
                        
                        log.debug("更新周规则 {} 的下次执行时间为: {}", rule.getRuleName(), rule.getNextExecuteTime());
                    }
                    
                } catch (Exception e) {
                    log.error("更新周规则 {} 时发生错误: {}", rule.getId(), e.getMessage(), e);
                }
            }
            
            log.info("每周规则检查完成");
            
        } catch (Exception e) {
            log.error("执行每周规则检查时发生错误: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 每月1号凌晨4点执行月规则检查
     */
    @Scheduled(cron = "0 0 4 1 * ?")
    public void monthlyRuleCheck() {
        try {
            log.info("开始执行每月规则检查...");
            
            // 获取所有启用的规则
            List<AutoAccountingRule> activeRules = autoAccountingRuleDao.selectActiveRules();
            
            for (AutoAccountingRule rule : activeRules) {
                try {
                    // 检查规则是否需要调整
                    if ("MONTHLY".equals(rule.getFrequency())) {
                        // 重新计算下月的执行时间
                        autoAccountingRuleService.calculateNextExecuteTime(rule);
                        autoAccountingRuleDao.update(rule);
                        
                        log.debug("更新月规则 {} 的下次执行时间为: {}", rule.getRuleName(), rule.getNextExecuteTime());
                    }
                    
                } catch (Exception e) {
                    log.error("更新月规则 {} 时发生错误: {}", rule.getId(), e.getMessage(), e);
                }
            }
            
            log.info("每月规则检查完成");
            
        } catch (Exception e) {
            log.error("执行每月规则检查时发生错误: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 异步执行规则
     */
    private void executeRuleAsync(AutoAccountingRule rule) {
        try {
            log.info("开始执行规则: {} (ID: {})", rule.getRuleName(), rule.getId());
            
            long startTime = System.currentTimeMillis();
            
            // 执行规则 - 使用同步方式避免事务问题
            AutoAccountingExecutionLog executionLog = autoAccountingRuleService.executeRule(rule.getId());
            
            long executionTime = System.currentTimeMillis() - startTime;
            
            if ("SUCCESS".equals(executionLog.getExecuteResult())) {
                log.info("规则 {} 执行成功，耗时: {}ms", rule.getRuleName(), executionTime);
            } else {
                log.warn("规则 {} 执行失败: {}", rule.getRuleName(), executionLog.getErrorMessage());
            }
            
        } catch (Exception e) {
            log.error("执行规则 {} 时发生错误: {}", rule.getId(), e.getMessage(), e);
        }
    }
    
    /**
     * 手动触发规则执行（用于测试或紧急情况）
     */
    public void triggerRuleExecution(String ruleId) {
        try {
            log.info("手动触发规则执行: {}", ruleId);
            
            CompletableFuture.runAsync(() -> {
                try {
                    AutoAccountingRule rule = autoAccountingRuleService.getRuleById(ruleId);
                    if (rule != null) {
                        executeRuleAsync(rule);
                    } else {
                        log.warn("规则 {} 不存在", ruleId);
                    }
                } catch (Exception e) {
                    log.error("手动触发规则 {} 执行时发生错误: {}", ruleId, e.getMessage(), e);
                }
            }, executorService);
            
        } catch (Exception e) {
            log.error("手动触发规则执行时发生错误: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 批量触发规则执行
     */
    public void triggerBatchRuleExecution(List<String> ruleIds) {
        try {
            log.info("批量触发规则执行，共 {} 个规则", ruleIds.size());
            
            for (String ruleId : ruleIds) {
                triggerRuleExecution(ruleId);
            }
            
        } catch (Exception e) {
            log.error("批量触发规则执行时发生错误: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 获取调度器状态
     */
    public String getSchedulerStatus() {
        return "自动记账调度器运行中 - 线程池大小: " + 
               ((java.util.concurrent.ThreadPoolExecutor) executorService).getCorePoolSize();
    }
    
    /**
     * 关闭调度器
     */
    public void shutdown() {
        try {
            log.info("正在关闭自动记账调度器...");
            executorService.shutdown();
            log.info("自动记账调度器已关闭");
        } catch (Exception e) {
            log.error("关闭自动记账调度器时发生错误: {}", e.getMessage(), e);
        }
    }
}
